﻿#region Namespace References

using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using DynConDevCorp.OpenArch.SmartData.Core.DisplayFormatters;
using DynConDevCorp.OpenArch.SmartData.Interfaces;
using DynConDevCorp.OpenArch.SmartData.Interfaces.EventArgClasses;

#endregion

namespace DynConDevCorp.OpenArch.SmartData.Core
{
    /// <summary>
    /// Base Concrete class for all Value Type invariant Field level operations
    /// </summary>
    public abstract class SmartField : ISmartField
    {
        public virtual void Dispatch(ISmartDispatcher dispatcher, object context)
        {
            dispatcher.ProcessDispatch(this, context);
        }

 
        #region ISmartField Members

        /// <summary>
        /// Commits this instance.
        /// </summary>
        public void Commit()
        {
            if (TransactionMode == TrackingMode.Dirty)
            {
                TransactionMode = TrackingMode.Committed;
            }
        }

        public string DisplayFormatString { get; set; }

        /// <summary>
        /// Occurs when [display value changed].
        /// </summary>
        public event EventHandler<DisplayValueChangedEventArgs> DisplayValueChanged;


        /// <summary>
        /// Enables the track changes.
        /// </summary>
        /// <param name="state">if set to <c>true</c> [state].</param>
        public void EnableTrackChanges(bool state)
        {
            TransactionMode = state ? TrackingMode.Committed : TrackingMode.NotTracking;
        }

        /// <summary>
        /// Formatteds the display.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <returns></returns>
        public abstract string FormattedDisplay(string format);

        /// <summary>
        /// Marks as dirty.
        /// </summary>
        public void MarkAsDirty()
        {
            if (TransactionMode == TrackingMode.Committed)
            {
                TransactionMode = TrackingMode.Dirty;
            }
        }

        /// <summary>
        /// Occurs when [raw value changed].
        /// </summary>
        public event EventHandler<ValueChangedEventArgs<object>> RawValueChanged;

        /// <summary>
        /// Rollback value to last Committed Value.
        /// </summary>
        public abstract void Rollback();

        /// <summary>
        /// Sets the entity which has direct ownership of this instance.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void SetOwningEntity(ISmartEntity entity)
        {
            m_OwningEntity = entity;
        }

        #endregion

        #region Public Instance Properties

        /// <summary>
        /// Gets the type of the data.
        /// </summary>
        /// <value>The type of the data.</value>
        public abstract Type DataType { get; }

        /// <summary>
        /// Gets the display value.
        /// </summary>
        /// <value>The display value.</value>
        public abstract string DisplayValue { get; }

        /// <summary>
        /// Gets or sets a value indicating whether [input inhibited].
        /// </summary>
        /// <value><c>true</c> if [input inhibited]; otherwise, <c>false</c>.</value>
        public PartialBoolean InputInhibited { get; set; }

        /// <summary>
        /// Sets the input value.
        /// </summary>
        /// <value>The input value.</value>
        public abstract string InputValue { set; }

        /// <summary>
        /// Gets a value indicating whether this instance is dirty.
        /// </summary>
        /// <value><c>true</c> if this instance is dirty; otherwise, <c>false</c>.</value>
        public PartialBoolean IsDirty
        {
            get { return TransactionMode == TrackingMode.Dirty; }
        }


        /// <summary>
        /// Gets the entity which has direct ownership of this instance.
        /// </summary>
        /// <value>The owning entity.</value>
        public ISmartEntity OwningEntity
        {
            get { return m_OwningEntity; }
        }


        /// <summary>
        /// Gets or sets the raw value.
        /// </summary>
        /// <value>The raw value.</value>
        public abstract object RawValue { get; set; }

        private PartialBoolean m_WriteLocked;

        /// <summary>
        /// Gets or sets a value indicating whether [read only].
        /// </summary>
        /// <value><c>true</c> if [read only]; otherwise, <c>false</c>.</value>
        public PartialBoolean WriteLocked
        {
            get { return m_WriteLocked; }
            set
            {
                m_WriteLocked = value;
                UpdateWriteLockedRule();
            }
        }

        /// <summary>
        /// Updates the read only rule.
        /// </summary>
        protected abstract void UpdateWriteLockedRule();

        /// <summary>
        /// Gets a value indicating whether [track changes].
        /// </summary>
        /// <value><c>true</c> if [track changes]; otherwise, <c>false</c>.</value>
        public PartialBoolean TrackingChanges
        {
            get { return !(TransactionMode == TrackingMode.NotTracking); }
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return DisplayValue;
        }

        #endregion

        #region Protected Instance Methods

        /// <summary>
        /// Fire_s the raw value changed.
        /// </summary>
        /// <param name="oldValue">The old value.</param>
        /// <param name="newValue">The new value.</param>
        protected void InvokeRawValueChanged(object oldValue, object newValue)
        {
            if (RawValueChanged != null)
            {
                ValueChangedEventArgs<object> args = new ValueChangedEventArgs<object>(this, oldValue, newValue);
                RawValueChanged(this, args);
            }
        }

        protected void InvokeDisplayValueChanged(string newValue)
        {
            if (DisplayValueChanged != null)
            {
                DisplayValueChangedEventArgs args = new DisplayValueChangedEventArgs(this, String.Empty, newValue);
                DisplayValueChanged(this, args);
            }
        }

        #endregion

        #region Fields and Constants

        private ISmartEntity m_OwningEntity;

        /// <summary>
        /// 
        /// </summary>
        protected TrackingMode TransactionMode { get; set; }

        #endregion
    }

    /// <summary>
    /// Base Concrete class for all Value Type Specific Field level operations
    /// </summary>
    /// <typeparam name="TData">The type of the AT a_ TYPE.</typeparam>
    public class SmartField<TData> : SmartField, ISmartField<TData>
            where TData : IComparable<TData>
    {

        public override void Dispatch(ISmartDispatcher dispatcher, object context)
        {
            dispatcher.ProcessDispatch(this, context);
        }


        private ISmartRule<TData> m_ReadOnlyRule;

        /// <summary>
        /// Updates the read only rule.
        /// </summary>
        protected override void UpdateWriteLockedRule()
        {
            if ((WriteLocked == PartialBoolean.True) && (m_ReadOnlyRule == null))
            {
                m_ReadOnlyRule = new SmartRule<TData>();
                AddRule(m_ReadOnlyRule);
            }

            if ((WriteLocked != PartialBoolean.True) && (m_ReadOnlyRule != null))
            {
                RemoveRule(m_ReadOnlyRule);
                m_ReadOnlyRule = null;
            }
        }

        #region ISmartField<TDATA> Members

        /// <summary>
        /// Formatteds the display.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <returns></returns>
        public override string FormattedDisplay(string format)
        {
            return Value.ToString();
        }

        /// <summary>
        /// Rollback value to last Committed Value.
        /// </summary>
        public override void Rollback()
        {
            if (TransactionMode == TrackingMode.Dirty)
            {
                Value = m_PreviousValue;
                TransactionMode = TrackingMode.Committed;
            }
        }

        /// <summary>
        /// Adds the effect.
        /// </summary>
        /// <param name="effect">The effect.</param>
        public void AddEffect(ISmartEffect<TData> effect)
        {
            m_Effects.Add(effect);
            ValueChangedEventArgs<TData> args = new ValueChangedEventArgs<TData>(this, default(TData), Value);
            effect.InvokeEffect(null, args);
        }

        /// <summary>
        /// Removes the effect.
        /// </summary>
        /// <param name="effect">The effect.</param>
        public void RemoveEffect(ISmartEffect<TData> effect)
        {
            m_Effects.Remove(effect);
        }

        /// <summary>
        /// Adds the rule.
        /// </summary>
        /// <param name="rule">The rule.</param>
        public void AddRule(ISmartRule<TData> rule)
        {
            m_Rules.Add(rule);
        }

        /// <summary>
        /// Removes the rule.
        /// </summary>
        /// <param name="rule">The rule.</param>
        public void RemoveRule(ISmartRule<TData> rule)
        {
            m_Rules.Remove(rule);
        }


        /// <summary>
        /// Occurs when [value changed].
        /// </summary>
        public event EventHandler<ValueChangedEventArgs<TData>> ValueChanged
        {
            add
            {
                ISmartEffect<TData> effect = new SmartEffect<TData>(value);
                AddEffect(effect);
            }
            remove
            {
                ISmartEffect<TData> effect = new SmartEffect<TData>(value);
                RemoveEffect(effect);
            }
        }

        #endregion

        #region Public Instance Properties

        /// <summary>
        /// Gets the type of the data.
        /// </summary>
        /// <value>The type of the data.</value>
        public override Type DataType
        {
            get { return typeof (TData); }
        }

        /// <summary>
        /// Gets or sets the display formatter.
        /// </summary>
        /// <value>The display formatter.</value>
        public ISmartDisplayFormatter<TData> DisplayFormatter { get; set; }

        /// <summary>
        /// Gets the display value.
        /// </summary>
        /// <value>The display value.</value>
        public override string DisplayValue
        {
            get
            {
                ISmartDisplayFormatter formatter = DisplayFormatterFactory.GlobalInstance.GetDefaultFormatter<TData>();
                return formatter.Format(Value, DisplayFormatString);
            }
        }

        /// <summary>
        /// Gets or sets the input parser.
        /// </summary>
        /// <value>The input parser.</value>
        public ISmartInputParser<TData> InputParser { get; set; }

        /// <summary>
        /// Sets the input value.
        /// </summary>
        /// <value>The input value.</value>
        public override string InputValue
        {
            set
            {
                TData input = (TData)Convert.ChangeType(value, typeof(TData), CultureInfo.InvariantCulture);
                Value = input;
            }
        }

        /// <summary>
        /// Gets or sets the raw value.
        /// </summary>
        /// <value>The raw value.</value>
        public override object RawValue
        {
            get { return Value; }
            set
            {
                TData typedValue = (TData) Convert.ChangeType(value, typeof (TData), CultureInfo.InvariantCulture);
                Value = typedValue;
            }
        }

        /// <summary>
        /// Evaluates the rules.
        /// </summary>
        /// <param name="proposedValue">The proposed value.</param>
        /// <returns></returns>
        private bool EvaluateRules(TData proposedValue)
        {
            foreach (ISmartRule<TData> rule in m_Rules)
            {
                if (!rule.Evaluate(this, proposedValue))
                    return false;
            }
            return true;
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        public TData Value
        {
            get { return m_Value; }
            set
            {
                // ReSharper disable CompareNonConstrainedGenericWithNull
                if (m_Value != null)
                {
                    if (m_Value.Equals(value)) return;
                }
                else
                {
                    if (value == null) return;
                }
                // ReSharper restore CompareNonConstrainedGenericWithNull
                if (!EvaluateRules(value))
                    throw new ReadOnlyException("SmartField has WriteLocked Property Set");

                if (TransactionMode == TrackingMode.Committed)
                {
                    m_PreviousValue = m_Value;
                    TransactionMode = TrackingMode.Dirty;
                }
                TData oldValue = m_Value;
                m_Value = value;
                ProcessValueChanged(oldValue, m_Value);
            }
        }

        #endregion

        #region Private Instance Methods

        private void ProcessValueChanged(TData oldValue, TData newValue)
        {
            InvokeValueChanged(oldValue, newValue);
            InvokeRawValueChanged(oldValue, m_Value);
            InvokeDisplayValueChanged(DisplayValue);
        }

        private void InvokeValueChanged(TData oldValue, TData newValue)
        {
            ValueChangedEventArgs<TData> args = new ValueChangedEventArgs<TData>(this, oldValue, newValue);
            foreach (ISmartEffect<TData> effect in m_Effects)
            {
                effect.InvokeEffect(this, args);
            }
            if (OwningEntity != null)
            {
                OwningEntity.PropogateFieldEffects(this, args);
            }
        }

        #endregion

        #region Fields and Constants

        private readonly List<ISmartEffect<TData>> m_Effects = new List<ISmartEffect<TData>>();
        private readonly List<ISmartRule<TData>> m_Rules = new List<ISmartRule<TData>>();
        private TData m_PreviousValue;
        private TData m_Value;

        #endregion
    }
}